// PR c++/91361 - P1152R4: Deprecating some uses of volatile.
// { dg-do compile { target c++20 } }
// { dg-options "-Wno-deprecated" }

#define ACCESS_ONCE(x) (*(volatile __typeof(x) *)&(x))

struct S {
  volatile int a : 4;
  int b : 2;
};

struct T {
  int a : 4;
  int b : 2;
};

union U {
  char c;
  int i;
};

struct W {
  W();
  W(volatile W&);
  W& operator=(volatile W&) volatile;
};

volatile int
fn (volatile int i)
{
  volatile int v = 10;
  int *volatile p = nullptr;

  // Pre/post ++/--.
  v++;
  ++v;
  v--;
  --v;
  p++;
  ++p;
  p--;
  --p;
  return v + i + *p;
}

void
fn2 ()
{
  volatile int vi = 42;
  int i = 24;

  // Discarded-value expression ([expr.context]).
  // The lvalue-to-rvalue conversion is applied here:
  vi;
  // ...but not here.  Otherwise we'd write to VI and then immediately read it.
  vi = 42;
  vi = i;
  vi = i = 42;
  i = vi = 42;
  &(vi = i);
  (vi = 42, 45);
  (i = vi = 42, 10);
  i = vi; // LHS not volatile.
  i = (vi = i, 42);
  static_cast<void>(vi = i);
  static_cast<void>(i = vi = 42);
  (void)(vi = i);
  (void)(i = vi = 42);

  // Unevaluated operand.
  decltype(vi = 42) x = vi;
  decltype(i = vi = 42) x3 = i;

  // Compound assignments.
  vi += i;
  vi -= i;
  vi %= i;
  vi ^= i;
  vi |= i;
  vi /= i;
  vi = vi += 42;
  vi += vi = 42;
  i *= vi;
  decltype(vi -= 42) x2 = vi;

  // Structured bindings.
  int a[] = { 10, 5 };
  const auto & [cxr, cyr] = a;
  const volatile auto & [cvxr, cvyr] = a;
  volatile auto & [vxr, vyr] = a;
}

void
fn3 ()
{
  volatile int i, j, k = 0;
  i = j = k;

  ACCESS_ONCE(j);

  S s;
  s.b = 1;

  volatile U u;
  u.c = 42;
  i = u.c = 42;
  u.c += 42;

  volatile T t;
  t.a = 3;
  j = t.a = 3;
  t.a += 3;

  volatile int *src = &i;
  *src; // No assignment, don't warn.
}

void
fn4 ()
{
  volatile W vw;
  W w;
  // Assignment to objects of a class is defined by the copy/move assignment
  // operator.
  vw = w;
  w = vw;
}

template<typename T>
void raccoon ()
{
  volatile T t, u;
  t = 42;
  u = t = 42;
  t &= 42;
}

void
fn5 ()
{
  raccoon<int>();
}
